#if UNITY_EDITOR
using Devil;
using LitJson;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;

namespace GameToolkit
{

    public delegate Result Wrapper<Result>();
    public delegate Result Wrapper<Arg1, Result>(Arg1 arg);
    public delegate Result Wrapper<Arg1, Arg2, Result>(Arg1 arg1, Arg2 arg2);

    //public class InstanceWrapper<Instance, T> where T : System.Delegate
    //{
    //    protected MethodInfo mMethod;
    //    internal InstanceWrapper(MethodInfo method)
    //    {
    //        mMethod = method;
    //    }

    //    public T GetDelegate(Instance inst)
    //    {
    //        return (T)System.Delegate.CreateDelegate(typeof(T), inst, mMethod);
    //    }
    //}

    /// <summary>
    /// 通过反射的方式调用外部类型的非公开方法（仅限编辑器使用）
    /// </summary>
    [System.AttributeUsage(System.AttributeTargets.Field)]
    public class WrapExtenalAttribute : System.Attribute
    {
        public string TypeName { get; private set; }
        public System.Type UnityType { get; private set; }
        public WrapExtenalAttribute(System.Type type)
        {
            UnityType = type;
        }
        public WrapExtenalAttribute(string typeName)
        {
            TypeName = typeName;
        }
    }

    public static class UnityEngineWrap
    {
        public static ParallelDispatcher.Handler wrapProcess { get; private set; }

        [InitializeOnLoadMethodParallel(ExecutionOrder = InitializeOrder.First - 1)]
        internal static void WrapUnityEngine()
        {
            try
            {
                var delType = typeof(System.Delegate);
                //var types = new List<System.Type>();
                //GlobalUtil.GetAssemblyTypes(types);
                var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
                Dictionary<System.Type, MethodInfo[]> methods = new Dictionary<System.Type, MethodInfo[]>();

                var assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
                foreach (var assembly in assemblies)
                {
                    var types = assembly.GetTypes();
                    foreach (var type in types)
                    {
                        var fields = type.GetFields(flags);
                        for (int i = 0; i < fields.Length; i++)
                        {
                            var field = fields[i];
                            if (!field.FieldType.IsSubclassOf(delType))
                                continue;
                            var wrapTypes = field.GetCustomAttributes<WrapExtenalAttribute>();
                            if (wrapTypes == null)
                                continue;
                            var mtdInfo = field.FieldType.GetMethod("Invoke");
                            if (mtdInfo == null)
                                continue;
                            var margs = mtdInfo.GetParameters();
                            var margLen = margs == null ? 0 : margs.Length;
                            var mreturn = mtdInfo.ReturnType;
                            foreach (var wrap in wrapTypes)
                            {
                                var utype = wrap.UnityType;
                                if (utype == null && !string.IsNullOrEmpty(wrap.TypeName))
                                    utype = System.Type.GetType(wrap.TypeName, false);
                                if (utype == null)
                                    continue;
                                MethodInfo[] mtds;
                                if (!methods.TryGetValue(utype, out mtds))
                                {
                                    mtds = utype.GetMethods(flags);
                                    methods.Add(utype, mtds);
                                }
                                for (int m = 0; m < mtds.Length; m++)
                                {
                                    var mtd = mtds[m];
                                    var match = mtd.Name == field.Name;
                                    if (match)
                                    {
                                        var args = mtd.GetParameters();
                                        var size = args == null ? 0 : args.Length;
                                        match = size == margLen;
                                        for (int a = 0; match && a < size; a++)
                                        {
                                            if (args[a].ParameterType != margs[a].ParameterType)
                                            {
                                                match = false;
                                            }
                                        }
                                        if (match)
                                            match = mtd.ReturnType == mreturn;
                                    }
                                    if (match)
                                    {
                                        field.SetValue(type, System.Delegate.CreateDelegate(field.FieldType, mtd));
                                        Debug.LogFormat("Wrap UnityEngine: {0}.{1} ({2})", utype.Name, field.Name, type.Name);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

            }
            catch (System.Exception e)
            {
                Debug.LogException(e);
            }
        }

        #region exportor
        [MenuItem("Assets/Export/Export Package Extensions")]
        static void ExportExtensionFiles()
        {
            var workFolder = GlobalUtil.ActiveProjectFolder;
            string extensionFile = null;
            string packName = "PackExt";
            var isPack = workFolder.StartsWithIgnoreCase("Packages/");
            if (isPack)
            {
                var pack = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(workFolder);
                packName = pack.name;
                workFolder = pack.resolvedPath;
                var file = NativeFileSystem.Combine(workFolder, "extension.json");
                if (NativeFileSystem.HasFile(file))
                    extensionFile = file;
            }
            else
            {
                var rootFolder = "Assets";
                while (!string.IsNullOrEmpty(workFolder))
                {
                    var file = NativeFileSystem.Combine(workFolder, "extension.json");
                    if (NativeFileSystem.HasFile(file))
                    {
                        packName = NativeFileSystem.GetFolderName(file);
                        extensionFile = file;
                        break;
                    }
                    else
                    {
                        workFolder = NativeFileSystem.GetFolderName(workFolder);
                        if (workFolder.EqualsIgnoreCase(rootFolder) || workFolder == ".")
                            break;
                    }
                }

            }
            if (string.IsNullOrEmpty(extensionFile))
            {
                EditorUtility.DisplayDialog("Error", "没有找到导出定义文件(extension.json)!", "确定");
                return;
            }
            var destFolder = EditorUtility.OpenFolderPanel("Export Extensions", "../", packName);
            if (string.IsNullOrEmpty(destFolder))
                return;
            if (!string.IsNullOrEmpty(NativeFileSystem.CalculateRelativePath(workFolder, destFolder)))
            {
                EditorUtility.DisplayDialog("Error", $"导出目标文件夹不能作为工作文件夹({workFolder})的子目录!", "确定");
                return;
            }
            EditorUtility.DisplayProgressBar("Export Extensions", "Get Export Files...", 0);
            bool finished = false;
            try
            {
                var dt = NativeFileSystem.ReadAllText(extensionFile);
                var obj = JsonMapper.ToObject(dt);
                HashSet<string> files = new HashSet<string>();
                HashSet<string> folders = new HashSet<string>();
                foreach (JsonData content in ((IDictionary)obj).Values)
                {
                    var arr = content["files"];
                    if (arr != null && arr.IsArray)
                    {
                        for (int i = 0; i < arr.Count; i++)
                        {
                            var file = (string)arr[i];
                            var path = NativeFileSystem.Combine(workFolder, file);
                            var fname = NativeFileSystem.GetFileName(path, true);
                            if (fname.Contains('*'))
                            {
                                var root = NativeFileSystem.Combine(workFolder, file);
                                root = NativeFileSystem.GetFolderName(root);
                                var matchFiles = Directory.GetFiles(root, fname, SearchOption.AllDirectories);
                                foreach (var f in matchFiles)
                                {
                                    file = NativeFileSystem.CalculateRelativePath(workFolder, f);
                                    if (!string.IsNullOrEmpty(file))
                                    {
                                        path = NativeFileSystem.Combine(destFolder, file);
                                        files.Add(file);
                                        folders.Add(NativeFileSystem.GetFolderName(path));
                                    }
                                }
                            }
                            else
                            {
                                if (!NativeFileSystem.HasFile(path))
                                {
                                    Debug.LogErrorFormat("Missing file(\"{0}\") and skiped.", file);
                                    continue;
                                }
                                path = NativeFileSystem.Combine(destFolder, file);
                                files.Add(file);
                                folders.Add(NativeFileSystem.GetFolderName(path));
                            }
                        }
                    }
                }
                EditorUtility.DisplayProgressBar("Export Extensions", "Clean Folder...", 0.1f);

                var existFolders = Directory.GetDirectories(destFolder, "*", SearchOption.AllDirectories);
                foreach (var folder in existFolders)
                {
                    if (!Directory.Exists(folder))
                        continue;
                    bool inUse = false;
                    foreach (var usingFolder in folders)
                    {
                        if (!string.IsNullOrEmpty(NativeFileSystem.CalculateRelativePath(folder, usingFolder)))
                        {
                            inUse = true;
                            break;
                        }
                    }
                    if (!inUse)
                        Directory.Delete(folder, true);
                }
                var existFiles = Directory.GetFiles(destFolder, "*", SearchOption.AllDirectories);
                foreach (var f in existFiles)
                {
                    File.Delete(f);
                }
                foreach (var usingFolder in folders)
                {
                    NativeFileSystem.EnsureDirectory(usingFolder);
                }

                var num = 0;
                var factor = files.Count == 0 ? 1f : (1f / files.Count) * 0.9f;
                foreach (var file in files)
                {
                    EditorUtility.DisplayProgressBar("Export Extensions", $"Export File: {file}", 0.1f + (num++) * factor);
                    var path = NativeFileSystem.Combine(workFolder, file);
                    var dest = NativeFileSystem.Combine(destFolder, file);
                    NativeFileSystem.CopyFile(path, dest);
                    path = $"{path}.meta";
                    if (NativeFileSystem.HasFile(path))
                    {
                        dest = $"{dest}.meta";
                        NativeFileSystem.CopyFile(path, dest);
                    }
                }
                finished = true;
            }
            catch (System.Exception e)
            {
                Debug.LogException(e);
            }
            EditorUtility.ClearProgressBar();
            EditorUtility.DisplayDialog("Export Extensions", finished ? $"文件已从\"{workFolder}\"导出到\"{destFolder}\"!" : "导出失败！", "确定");
        }
        #endregion


        #region wrappers

       
        #endregion

    }
}
#endif